package com.nado.extensions.util;

import java.io.Serializable;

/**
 * (Messaging) Domain Model 인터페이스.
 * <p/>
 * FIELD와 COMMAND로 구성되며, 정의상 COMMAND는 FIELD[]를 반환하는 몇 개의 API를 구현하도록 되어 있다.
 * <p/>
 * COMMAND는 해당 COMMAND를 처리할 때 반드시 필요한 필드(required)와 옵션으로 사용할 수도 있는 필드(optional)를 정의하도록 되어 있으며
 * 여기에 지정된 필드는 이후 DomainModel을 이용하여 DomainMap을 validation할 때 사용된다.
 * <p/>
 * COMMAND의 실행 결과를 얻기 위한 필드(result)를 지정할 수 있으나, 단순히 해당 COMMAND의 스펙으로 간주되며
 * validation에 사용되지는 않는다.
 * <p/>
 * DomainModel의 FIELD와 COMMAND는 각각 하나의 Enum 타입으로 구현하는 것이 기본이지만, 의도에 따라 Enum 타입을 사용하지 않거나
 * 두 개 이상의 Enum 타입을 이용하여 구현할 수도 있다(이 부분은 추가 작업이 있을 예정).
 *
 */
public interface DomainModel<F extends Enum & DomainModel.FIELD, C extends Enum & DomainModel.COMMAND> extends Serializable {

	/**
	 * DomainMap에 사용되는 필드 타입
	 */
	interface FIELD {
		String name();
	}

	/**
	 * expansion for NBoard2
	 */
	interface VALIDATE extends FIELD {
		String[] expected();
		String pattern();
		String errorMsgKey();
	}

	/**
	 * DomainMap을 통해 처리하고자 하는 COMMAND의 스펙
	 */
	interface COMMAND {
		String name();
		FIELD[] required();
		FIELD[] optional();
		FIELD[] results();
	}

	FIELD[] fields();
	FIELD[] required(COMMAND command);
	FIELD[] optional(COMMAND command);
	FIELD[] results(COMMAND command);

	COMMAND[] commands();

	void validateFields(DomainMap<?> target);
	void validateRequired(COMMAND command, DomainMap<?> target);
	void validateExclusive(COMMAND command, DomainMap<?> target);

	boolean equals(COMMAND command, DomainMap<?> target1, DomainMap<?> target2);
	String toString(COMMAND command, DomainMap<?> target);

	boolean isRequired(COMMAND command, FIELD field);
	boolean isOptional(COMMAND command, FIELD field);

	FIELD domainField(String fieldName);
	COMMAND domainCommand(String commandName);

	// these are optional implementation for enum type DomainModels
	// throws exception for non-enum type DomainModels
	F resolve(FIELD field);
	C resolve(COMMAND command);
	F resolveField(String fieldName);
	C resolveCommand(String commandName);
}
